package at.fhooe.mcm12.java.sensors;

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.List;

import at.fhooe.mcm12.java.enums.SensorType;
import at.fhooe.mcm12.java.interfaces.ISensorListener;
import at.fhooe.mcm12.java.interfaces.RMISensorControllerInterface;
import at.fhooe.mcm12.java.interfaces.RMISensorInterface;
import at.fhooe.mcm12.java.sensors.data.SensorValue;
import at.fhooe.mcm12.java.utils.TimeUtils;

public class SensorController extends UnicastRemoteObject implements
		ISensorListener, Runnable, RMISensorControllerInterface {

	private static final long serialVersionUID = 1L;
	private ArrayList<RMISensorInterface> m_sensors;
	private int m_pollInterval;
	private ArrayList<ISensorListener> m_listeners;

	public SensorController() throws RemoteException {
		initiateRMIServer();
		
		m_sensors = new ArrayList<RMISensorInterface>();
		m_pollInterval = 5000;
		m_listeners = new ArrayList<ISensorListener>();
		
		GPSSensor gpsSensor = new GPSSensor();
		gpsSensor.setUpdateInterval(2000);
		
		TemperatureSensor temperatureSensor = new TemperatureSensor();
		temperatureSensor.setUpdateInterval(1000);
		
		LightSensor lightSensor = new LightSensor();
		lightSensor.setUpdateInterval(1500);
		
		HumiditySensor humiditySensor = new HumiditySensor();
		PressureSensor pressureSensor = new PressureSensor();
		
		List<AbstractSensor> list = new ArrayList<AbstractSensor>();
		list.add(gpsSensor);
		list.add(temperatureSensor);
		list.add(lightSensor);
		list.add(humiditySensor);
		list.add(pressureSensor);
		
		initiateRMIClients(list);

		Thread t = new Thread(this);
		t.start();
	}
	
	private void initiateRMIClients(List<AbstractSensor> sensors) {
		System.setProperty("java.security.policy", "file:./res/rmi.policy");
		if (System.getSecurityManager() == null) {
			System.setSecurityManager(new SecurityManager());
		}

		for (AbstractSensor sensor : sensors) {
			try {
				Registry remoteRegistry = LocateRegistry.getRegistry(RMISensorInterface.HOST, Registry.REGISTRY_PORT);
				addSensor((RMISensorInterface) remoteRegistry.lookup(sensor.getClass().getSimpleName()));
			} catch (RemoteException | NotBoundException e) {
				e.printStackTrace();
			}
		}
	}

	private void initiateRMIServer() {
		try {

			if (System.getSecurityManager() == null) {
				System.setProperty("java.security.policy", "file:./res/rmi.policy");
				System.setSecurityManager(new SecurityManager());
			}
			
			UnicastRemoteObject.unexportObject(this, true);

			RMISensorControllerInterface stub = (RMISensorControllerInterface) UnicastRemoteObject.exportObject(this,
					0);
			Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);

			registry.rebind(RMISensorControllerInterface.NAME, stub);
			System.out.println("Registered: " + "SensorController" + " -> "
					+ this.getClass().getName() + "[" + this + "]");

		} catch (Exception _e) {
			_e.printStackTrace();
		}
	}

	@Override
	public void addListener(ISensorListener l) {
		m_listeners.add(l);
	}

	@Override
	public void removeListener(ISensorListener l) {
		m_listeners.remove(l);
	}

	public ArrayList<RMISensorInterface> getActiveSensors() {
		ArrayList<RMISensorInterface> activeSensors = new ArrayList<RMISensorInterface>();

		for (RMISensorInterface sensor : m_sensors) {
			try {
				if (sensor.isActive()) {
					activeSensors.add(sensor);
				}
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}

		return activeSensors;
	}

	@Override
	public ArrayList<RMISensorInterface> getSubscribableSensors() {
		ArrayList<RMISensorInterface> subscribableSensors = new ArrayList<RMISensorInterface>();

		for (RMISensorInterface sensor : m_sensors) {
			try {
				if (sensor.isSubscribable()) {
					subscribableSensors.add(sensor);
				}
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}

		return subscribableSensors;
	}

	@Override
	public void enableSensor(RMISensorInterface _sensor) {
		if (_sensor != null && m_sensors.contains(_sensor)) {
			try {
				_sensor.setActive(true);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public void disableSensor(RMISensorInterface _sensor) {
		if (_sensor != null && m_sensors.contains(_sensor)) {
			try {
				_sensor.setActive(false);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	}

	public void addSensor(RMISensorInterface _sensor) {
		if (_sensor != null && !m_sensors.contains(_sensor)) {
			m_sensors.add(_sensor);

			try {
				if (_sensor.getSensorType() == SensorType.Push) {
					_sensor.addSensorListener(this);
				}
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	}

	public void removeSensor(RMISensorInterface _sensor) {
		if (_sensor != null && m_sensors.contains(_sensor)) {
			m_sensors.remove(_sensor);
		}
	}

	public void setSubscribable(RMISensorInterface _sensor, boolean _isSubscribable) {
		if (_sensor != null && m_sensors.contains(_sensor)) {
			try {
				_sensor.setSubscribable(_isSubscribable);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	}

	public int getPollIntervall() {
		return m_pollInterval;
	}

	public void setPollIntervall(int _pollInterval) {
		m_pollInterval = _pollInterval;
	}

	@Override
	public void onSensorValueReceived(String _sensor, SensorValue _value) {
		if (_value != null) {
//			System.out.println(TimeUtils.getCurrentTimeStamp(_value
//					.getTimestamp()) + ": " + _value.toString());

			for (ISensorListener l : m_listeners) {
				try {
					l.onSensorValueReceived(_sensor, _value);
				} catch (RemoteException e) {
					e.printStackTrace();
				}
			}
		}
	}

	//TODO: implement with RMI support
	@Override
	public void run() {
		while (true) {
			try {
				
				try {
					for (RMISensorInterface sensor : m_sensors) {
						
							if (sensor.getSensorType() == SensorType.Poll && sensor.isActive()) {
								SensorValue value = sensor.getDataType();
								onSensorValueReceived(sensor.getName(), value);
							}
					}
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				Thread.sleep(m_pollInterval);
			} catch (InterruptedException ie) {
				ie.printStackTrace();
			}
		}
	}
}
